package com.mar.tech.unify.http;

import com.mar.tech.unify.RouterVerticle;
import com.mar.tech.unify.factory.UnifyFactory;
import com.mar.tech.unify.handler.RateLimitHandler;
import com.mar.tech.unify.handler.ResultFailureHandler;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.dropwizard.MetricsService;
import io.vertx.ext.healthchecks.HealthCheckHandler;
import io.vertx.ext.healthchecks.Status;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.JWTAuthHandler;
import io.vertx.ext.web.handler.StaticHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Description: 启动 Http Server .<br>
 *
 * @author m-xy
 *     Created By 2019/12/3 17:11
 */
public class HttpVerticle extends AbstractVerticle implements RouterVerticle {
    private final static Logger logger = LoggerFactory.getLogger(HttpVerticle.class);
    public static final String CONNECTION_RESET_BY_PEER = "Connection reset by peer";
    private MetricsService metricsService;

    @Override
    public void start(Promise<Void> promise) throws Exception {
        metricsService = MetricsService.create(vertx);
        final DeploymentOptions options = new DeploymentOptions()
            .setWorker(true)
            .setConfig(config());
        final JsonObject verticles = config().getJsonObject("verticles", new JsonObject());
        List<Future> futures = new ArrayList<>();
        verticles.fieldNames().forEach(key -> {
            futures.addAll(verticles.getJsonArray(key).stream().map(name -> Future.<String>future(r -> {
                vertx.deployVerticle(name.toString(), options, r);
                logger.info("[Vert.x] Verticles Ready : " + name.toString());
            })).collect(Collectors.toList()));
        });
        CompositeFuture.all(futures).onComplete(res -> {
            if (res.failed()) {
                promise.fail(res.cause());
                return;
            }
            logger.info("[Vert.x] All Verticles Loaded!");
            Router mainRouter = Router.router(vertx);
            enableCors(mainRouter);
            enableRouter(mainRouter);
            enableHttpServer(res, mainRouter);
            promise.complete();
        });
    }

    /**
     * 设置CORS跨域.
     *
     * @param mainRouter
     */
    private void enableCors(Router mainRouter) {
        Set<String> allowHeaders = new HashSet<>();
        allowHeaders.add("X-Requested-With");
        allowHeaders.add("Access-Control-Allow-Origin");
        allowHeaders.add("Access-Control-Request-Headers");
        allowHeaders.add("Access-Control-Request-Method");
        allowHeaders.add("Origin");
        allowHeaders.add("Content-Type");
        allowHeaders.add("Accept");
        allowHeaders.add("Authorization");
        Set<HttpMethod> allowMethods = new HashSet<>();
        allowMethods.add(HttpMethod.GET);
        allowMethods.add(HttpMethod.POST);
        allowMethods.add(HttpMethod.DELETE);
        allowMethods.add(HttpMethod.PATCH);
        allowMethods.add(HttpMethod.OPTIONS);
        mainRouter.route().handler(
            CorsHandler.create("*")
                .allowCredentials(false)
                .allowedMethods(allowMethods)
                .allowedHeaders(allowHeaders)
        );
    }

    /**
     * 初始化 Router.
     *
     * @param mainRouter 路由
     */
    private void enableRouter(Router mainRouter) {
        String contextPath = config().getString("context-path", "/");
        logger.info("[Vert.x] Http server context-path is {} in Vert.x", contextPath);
        Router router = Router.router(vertx);
        router.route("/metrics/*").handler(StaticHandler.create());
        router.get("/health*").handler(enableHealthChecks());
        router.patch("/metrics/data").handler(enableMetrics());
        JWTAuthHandler jwtAuthHandler = JWTAuthHandler.create(UnifyFactory.jwt());
        BodyHandler bodyHandler = BodyHandler.create();
        RateLimitHandler rateLimitHandler = RateLimitHandler.create();
        ROUTERS.forEach(r -> {
            String api = r.getApi();
            Route route = router.route(r.getHttpMethod(), api);
            if (r.isAuth()) {
                route.handler(jwtAuthHandler);
            }
            if (r.getHttpMethod().equals(HttpMethod.POST)) {
                route.handler(bodyHandler);
            }
            // 可以加限流
            // route.handler(rateLimitHandler);
            route.handler(r.getHandler()).failureHandler(new ResultFailureHandler());
            logger.info("[Vert.x] Router register [{}] [{}]", r.getHttpMethod(), api);
        });
        mainRouter.mountSubRouter(contextPath, router);
        logger.info("[Vert.x] Router register success, summery {} mainRouter", ROUTERS.size());
    }

    /**
     * 初始化 Http Server.
     *
     * @param res    资源
     * @param router 路由
     */
    private void enableHttpServer(AsyncResult<CompositeFuture> res, Router router) {
        Integer port = config().getInteger("port", 8080);
        HttpServerOptions options = new HttpServerOptions()
            .setPort(port)
            .setLogActivity(false)
            .setCompressionSupported(true)
            .setHandle100ContinueAutomatically(true)
            .setTcpFastOpen(true)
            .setTcpNoDelay(true)
            .setTcpQuickAck(true);
        vertx.createHttpServer(options)
            .requestHandler(router)
            .exceptionHandler(ar -> {
                if (CONNECTION_RESET_BY_PEER.equals(ar.getMessage())) {
                    logger.warn("Http 服务发生了异常:{}", CONNECTION_RESET_BY_PEER);
                } else {
                    logger.error("Http 服务发生了异常", ar);
                }
            }).listen(httpServerAsyncResult -> {
            if (httpServerAsyncResult.succeeded()) {
                logger.info("[Vert.x] Http server started success : {}", port);
            } else {
                logger.error("[Vert.x] Http server started fail", httpServerAsyncResult.cause());
            }
        });
    }

    /**
     * 健康检查.
     * 访问 http://127.0.0.1:8082/app/health
     */
    private HealthCheckHandler enableHealthChecks() {
        HealthCheckHandler healthCheckHandler = HealthCheckHandler.create(vertx);
        healthCheckHandler.register("database",
            future -> UnifyFactory.mysql().query("select 1").execute(res -> {
                if (res.failed()) {
                    future.fail(res.cause());
                } else {
                    future.complete(Status.OK());
                }
            }));
        healthCheckHandler.register("application", future -> {
            future.complete(Status.OK());
        });
        logger.info("[Vert.x] Health Checks");
        return healthCheckHandler;
    }

    private Handler<RoutingContext> enableMetrics() {
        return routingContext -> {
            try {
                JsonObject metrics = metricsService.getMetricsSnapshot(vertx);
                routingContext.response().end(metrics.encodePrettily());
            } catch (Exception e) {
                logger.info("监控异常", e);
                routingContext.fail(e);
            }
        };
    }
}

